package websocket;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ReceivingThread extends Thread
{
    private final static Logger LOGGER = Logger.getLogger(ReceivingThread.class
        .getName());
    
    private WebSocketConnection wsConnection;
    private Socket client;
    
    public ReceivingThread(WebSocketConnection connection, Socket client)
    {
        if (connection == null || client == null)
        {
            throw new IllegalArgumentException("WebsocketConnection or Client socket is null");
        }
        this.wsConnection = connection;
        this.client = client;
    }
    
    @Override
    public void run()
    {
        super.run();
        
        Framedata frame;
        StringBuilder combinedMessage = null;
        
        // What about stopping receiving thread when an error will show up while sending? or when I manually want to remove the client?
        // When error will occur, then reading should throw IOException or SocketException
        
        try(InputStream in = client.getInputStream())
        {
            try
            {
                while (true)
                {
                    client.setSoTimeout(0);
                    frame = Framedata.getFrame(in);
                    

                    LOGGER.log(Level.FINE, "A frame from {0}:{1} was received. Frame type: {2}",
                               new Object[]{client.getInetAddress(), client.getPort(), frame.getOpcode()});
                    
                    if (frame.isTextDataFrame())
                    {
                        if (frame.isFinFrame())
                        {
                            wsConnection.onMessage(frame.getTextData());
                        }
                        else
                        {
                            // Message is splitted into more than one frame
                            if (combinedMessage != null)
                            {
                                // Wrong sequence of frames
                                wsConnection.onProtocolError("Wrong sequence of frames. Text data frame was received, but continuation frame was expected");
                                break;
                            }

                            combinedMessage = new StringBuilder(frame.getTextData());
                        }
                    }
                    else if (frame.isBinaryDataFrame())
                    {
                        // Not supported
                        wsConnection.onNotSupportedData();
                        break;
                    }
                    else if (frame.isContinuationFrame())
                    {
                        if (combinedMessage == null)
                        {
                            // Wrong sequence of frames
                            wsConnection.onProtocolError("Wrong sequence of frames. Continuation data was received, but no initial frame was there before");
                            break;
                        }

                        combinedMessage.append(frame.getTextData());

                        if (frame.isFinFrame())
                        {
                            wsConnection.onMessage(combinedMessage.toString());
                            combinedMessage = null;
                        }
                    }
                    else if (frame.isCloseFrame())
                    {
                        wsConnection.onCloseFrame(frame.getClosingReason());
                    }
                    else if (frame.isPingFrame())
                    {
                        wsConnection.onPingFrame();
                    }
                    else if (frame.isPongFrame())
                    {
                        wsConnection.onPongFrame();
                    }
                }
            }
            catch (UnsupportedOperationException ex)
            {
                // The message length is too big
                wsConnection.onMessageTooBig();
            }
            catch (WebSocketProtocolException ex)
            {
                // Opcode is invalid
                wsConnection.onProtocolError(ex.getMessage());
            }
        }
        catch (IOException ex)
        {
            LOGGER.log(Level.FINER, "IOException {0}", ex.getMessage());
            wsConnection.onBrokenConnection(ex);
        }
    }
    
}
